﻿#define _CRT_SECURE_NO_WARNINGS 1
#pragma once
#include<stdio.h>

int main()
{
	FILE* pf = fopen("text.txt", "r");
	if (pf == NULL)
	{
		perror("fopen");
		return 0;
	}
	写入字符
	char ch = 0;
	for (ch = 'a'; ch <= 'z'; ch++)
	{
		int ch = fgetc(pf);
		printf("%c ", ch);
	}

	fclose(pf);
	pf = NULL;
	return 0;
}

int main()
{
	FILE* pf = fopen("text.txt", "w");
	if (pf == NULL)
	{
		perror("fopen");
		return 0;
	}
	fputs("Hello World", pf);

	fclose(pf);
	pf = NULL;
	return 0;
}


int main()
{
	FILE* pf = fopen("text.txt", "r");
	if (pf == NULL)
	{
		perror("fopen");
		return 0;
	}
	写文件
	一次写一行
	int fputs(const char* str, FILE * stream);
	fputs("Hello World\n", pf);
	fputs("Hello bit\n", pf);
	读文件，一次读一行
	char buf[20];
	fgets(buf, 10, pf); // 读10个字节，相当于读9个，最后一个字节留着放\0
	如果一行不够要读取的个数，就不再读了
	printf("%s\n", buf);

	fgets(buf, 20, pf);
	fgets只处理一行
	printf("%s", buf);
	/*fclose(pf);
	pf = NULL;*/
	return 0;
}

int main()
{
	FILE* pf = fopen("text.txt", "w");
	if (pf == NULL)
	{
		perror("fopen");
		return 0;
	}
	struct S
	{
		char name[20];
		int age;
		float score;
	}s = {"zhangsan",20,99.5f};
	fprintf(pf,"%s %d %f", s.name, s.age, s.score);
	
	fclose(pf);
	pf = NULL;
	return 0;
}

struct S
{
	char name[20];
	int age;
	float score;
}s ;

int main()
{
	FILE* pf = fopen("text.txt", "r");
	if (pf == NULL)
	{
		perror("fopen");
		return 0;
	}

	fscanf(pf, "%s %d %f", s.name, &s.age, &s.score);
	printf("%s %d %f", s.name, s.age, s.score);
	fclose(pf);
	pf = NULL;
	return 0;
}

int main()
{
	int ch = fgetc(stdin);
	fputc(ch, stdout);
}

typedef struct S
{
	char name[20];
	int age;
	float score;
}S;

int main()
{
	S s = { 0 };
	FILE* pf = fopen("text.txt", "rb");
	if (pf == NULL)
	{
		perror("fopen");
		return 0;
	}
	fread(&s, sizeof(S), 1, pf);
	printf("%s %d %f", s.name, s.age, s.score);

	fclose(pf);
	pf = NULL;
	return 0;
}


程序环境和预处理
test.c --> test.exe -->运行

int main()
{
	FILE* pf = fopen("text.txt", "r");
	int ch = 0;
	if (pf == NULL)
	{
		perror("fopen");
	}
	else
	{
		ch = fgetc(pf);
		printf("%c\n", ch);//a
		ch = fgetc(pf);
		printf("%c\n", ch);//b
		ch = fgetc(pf);
		printf("%c\n", ch);//c
		rewind(pf);
		ch = fgetc(pf);
		printf("%c\n", ch);//a
	}
	return 0;
}

#include <stdio.h>
#include <stdlib.h>
int main(void)
{
	int c; // 注意：int，非char，要求处理EOF
	FILE* fp = fopen("test.txt", "r");
	if (!fp) {
		perror("File opening failed");
		return EXIT_FAILURE;
	}
	fgetc 当读取失败的时候或者遇到文件结束的时候，都会返回EOF
	while ((c = fgetc(fp)) != EOF) 
	{
		putchar(c);
	}
	判断是什么原因结束的
	if (ferror(fp))
		puts("I/O error when reading");
	else if (feof(fp))
		puts("End of file reached successfully");
	fclose(fp);
}

#include <stdio.h>
#include <windows.h>
VS2019 WIN10环境测试
int main()
{
	FILE* pf = fopen("test.txt", "w");
	fputs("abcdef", pf);//先将代码放在输出缓冲区
	printf("睡眠10秒-已经写数据了，打开test.txt文件，发现文件没有内容\n");
	Sleep(10000);
	printf("刷新缓冲区\n");
	fflush(pf);//刷新缓冲区时，才将输出缓冲区的数据写到文件（磁盘）
	注：fflush 在高版本的VS上不能使用了
	printf("再睡眠10秒-此时，再次打开test.txt文件，文件有内容了\n");
	Sleep(10000);
	fclose(pf);
	注：fclose在关闭文件的时候，也会刷新缓冲区
	pf = NULL;
	return 0;
}
extern int Add(int a, int b);

int main()
{
	int a = 10;
	int b = 20;
	int c = Add(a, b);
	printf("%d\n", c);
	return 0;
}

__FILE__    //进行编译的源文件
__LINE__   //文件当前的行号
__DATE__   //文件被编译的日期
__TIME__   //文件被编译的时间
__STDC__   //如果编译器遵循ANSI C，其值为1，否则未定义

int main()
{
	printf("%s\n", __FILE__); //代码所在的源文件的名称
	printf("%d\n", __LINE__);//代码所在行号
	printf("%s\n", __DATE__);//打印日期出来
	printf("%s\n", __TIME__);//打印时间出来

	写日志文件
	int i = 0;
	int arr[10] = { 0 };
	FILE* pf = fopen("log.txt", "w");
	if (pf == NULL)
	{
		perror("fopen");
	}
	else
	{
		for (i = 0; i < 10; i++)
		{
			arr[i] = i;
			fprintf(pf, "file:%s line:%d date:%s time:%s i = %d \n", __FILE__, __LINE__, __DATE__, __TIME__,arr[i]);
		}
	}
	return 0;
}

#define MAX 1000
#define reg register      //为 register这个关键字，创建一个简短的名字
#define do_forever for(;;)   //用更形象的符号来替换一种实现
#define CASE break;case     //在写case语句的时候自动把 break写上。
 如果定义的 stuff过长，可以分成几行写，除了最后一行外，每行的后面都加一个反斜杠(续行符)。
#define DEBUG_PRINT printf("file:%s\tline:%d\t \
             date:%s\ttime:%s\n" ,\
             __FILE__,__LINE__ ,    \
             __DATE__,__TIME__ ) 

#define MAX 100
在预处理的时候，MAX已经被换成了100
#define STR "hehe"
前面放符号，后面可以放任何内容

#define reg register

#define do_forever for(;;)

int main()
{
	/*reg int a;
	register int a;*/
	do_forever;
	不加分号是这样的:
	for (;;)
		return 0 ;
	此时return 0是for语句的内容
	加了分号是这样的：
	for (;;);
	此时return 0 是最后才执行的内容，for语句里面的内容是空，就会死循环
	int max = MAX;
	printf("%d\n", max);

	return 0;
}

#define 后面应不应该加;?
#define MAX 100;
int main() 
{
	int a = MAX;
	int a = 100;;//出问题了
	printf("%d\n", a);
	
	printf("%d\n", MAX);//出问题了
	报错报的是替换之后的错误，即预处理之后的错误
	return 0;
}


#define 定义宏

#define SQUARE(X) X*X // 宏是完全替换的，不是传参的
int main()
{
	int ret = SQUARE(5);
	5相当于X，即X*X。
	int ret = X * X;
	printf("ret = %d\n", ret);
	int ret = SQUARE(5 + 1);
	被替换成  5+1*5+1
	printf("%d\n", ret);

	int ret = SQUARE((5 + 1));
	这样写加了括号，才是5+1为一个整体
	printf("%d\n",ret);

	return 0;
}

#define DOUBLE(X) X+X
int main()
{
	int a = 5;
	int ret = 10 * DOUBLE(a); // 55
	
	#define DOUBLE(X) ((X)+(X))
	这样写才对
	printf("ret = %d\n",ret);
	return 0;
}

#define PRINT(X) printf("the value of "#X" is %d\n",X)
 #X == "X",X是传过来的a 或者 b
void print(int x)
{
	printf("the value of is %d\n", x);
}

int main()
{
	int a = 10;
	int b = 20;

	PRINT(a);
	PRINT(b);
	return 0;
}

int main()
{
	printf("hello world\n");

	printf("hello " "world\n");
	printf("he""llo " "world\n");

}

#define CAT(X,Y) X##Y
##允许把位于它两边的符号合成一个符号，它允许宏定义从分离的文本片段创建标识符。
int main()
{
	int Class84 = 2023;
	printf("%d\n", CAT(Cl, ass84));
	return 0;
}

int main()
{		
	int a = 10;
	int b = a + 1;//没有副作用
	printf("%d\n", b);
	b = ++a;// 有副作用
	printf("%d\n", b);

	return 0;
}

#define MAX(X,Y) (X)>(Y)?(X):(Y)   // 宏
牢记一点：宏是完全替换进去的，而不是计算好之后再替换进去
宏不会计算，是原模原样地替换进去

宏跟函数是很相似的

int MAX(int x, int y)
{
	return(x > y ? x : y);
}

float Max2(float x, float y)
{
	return (x > y ? x : y);
}


int main()
{
	int a = 10;
	int b = 20;
	float c = 3.0f ;
	float d = 4.0f ;
	函数在调用的时候，会有函数调用和返回的开销
	
	int max = MAX(a++, b++);

	max = (a++)>(b++)?(a++):(b++)
	宏在预处理阶段就完成了替换，没有函数的调用和返回的开销
	
 printf("%d\n", max);
	printf("%d\n", a);
	printf("%d\n", b);
	return 0;
}


#define test(x,y)
int main()
{

	return 0;

}

宏可以传类型，但是函数绝对传不了类型，比如宏可以传一个int，但是函数没有传一个int的说法
只能说传一个int类型的值

所以宏可以传一个类型
#define SIZEOF(type) sizeof(type)

int main()
{
	int ret = SIZEOF(int);
	替换成：
	int ret = sizeof(int);
	printf("%d\n", ret);
	return 0;
}

#define MALLOC(num,type) (type*)malloc(num*sizeof(type))
int main()
{
	int* p = MALLOC(10, int);
	int* p = malloc(10 * sizeof(int));
	return 0;
}

#define MAX 500
int main()
{
	printf("MAX = %d\n", MAX);
#undef MAX
	#undef 可以移除一个宏定义，下面再想打印MAX出来的时候
	编译器说不认识该标识符
	printf("MAX = %d\n", MAX);

	return 0;
}

命令行定义

int main()
{
	int arr[SZ] = { 0 };
	int i = 0;
	for (i = 0; i < SZ; i++)
	{
		arr[i] = i;
	}
	for (i = 0; i < SZ; i++)
	{
		printf("%d ", arr[i]);
	}

	return 0;
}

条件编译

#define DEBUG
现在DEBUG被定义了，下面条件编译为真，就执行下面的语句
int main()
{
	int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
	int i = 0;
	for (i = 0; i < 10; i++)
	{
#ifdef DEBUG
		如果这个DEBUG定义了，才会执行下面的语句
		printf("%d ", arr[i]);
#endif
	和ifdef是一对，意味着如果条件编译为真，它们里面的语句
		就会执行，否则，不执行
	}
}

常见的条件编译指令
1.常量表达式

#define DEBUG

int main()
{
	int arr[10] = { 0 };
	for (int i = 0; i < 10; i++)
	{
#ifdef DEBUG
		arr[i] = i;
#endif
		printf("%d ", arr[i]);
	}

	return 0;
}

int main()
{
#if 1
	printf("hehe\n");
#endif
	return 0;
}

2.多分支的判断语句

int main()
{
#if 1==1
	printf("hehe\n");
#elif 2==1
	printf("haha\n");
#else
	printf("heihei\n");
#endif
	return 0;
}

3.判断是否被定义

#if defined(symbol)
#endif (symbol)

#define DEBUG 0 //即使DEBUG被定义为0，为假，但是它已经被定义过了，就打印hehe
int main()
{
#if !defined(DEBUG)  // 只要定义过，不管定义什么,满足条件就参与编译
	printf("hehe\n");
#endif 
	return 0;
}

#ifdef DEBUG
printf("hehe\n");
#endif
return 0;
头文件被包含的两种方式：
#include<>
#include ""

1.使用库函数：#include < >
2.使用自定义函数:#include " "

1.#include <> 查找的时候，是直接从根目录下查找，
也就是在vs的include目录下查找。

2.#include "" 查找的时候，是先在自定义下的目录下查找，
再去库里面查找。

所以引头文件也可以使用 #include "stdio.h"，然而这样
会降低查找的效率，因为你明知道自定义目录下没有stdio.h这个头文件，还去找
明显会浪费时间

嵌套头文件包含解决方案：

1.
#ifndef __TEST_H__
#define __TEST_H__
头文件的内容 比如
int Add(int x, int y);

#endif  //__TEST_H__

#include"test.h"
#include"test.h"
#include"test.h"

解读：第一个ifndef：如果没有define后面的内容，那就define
下一次再进入头文件时，就不会进入该条件编译

2.
#pragma once

请编写宏，计算结构体中某变量相对于首地址的偏移，并给出说明。

#include<stddef.h>

struct S
{
	char c1;
	int a;
	char c2;
};

size_t offsetof( structName, memberName );

int main()
{
	struct S s = { 0 };
	int a1 = offsetof(struct S,c1);
	printf("%d\n", a1);
	int a2 = offsetof(struct S, a);
	printf("%d\n", a2);
	int a3 = offsetof(struct S, c2);
	printf("%d\n", a3);

	return 0;
}

模拟实现offsetof

struct S
{
	char c1;
	int a;
	char c2;
};

#define OFFSETOF(struct_name,member_name) (int)&(((struct_name*)0)->member_name)
										把0强转成结构体指针
int main()
{
	printf("%d\n", OFFSETOF(struct S, c1));
	上面与下面等价
	printf("%d\n", (int)&((struct S*)0)->c1);
	printf("%d\n", OFFSETOF(struct S, a));
	printf("%d\n", OFFSETOF(struct S, c2));

	return 0;
}


extern Add(int a, int b);

int main()
{
	printf("%d\n", Add(2, 5));
}

int main()
{
	printf("%s\n", __FILE__);//进行编译的源文件
	printf("%d\n", __LINE__);//显示当前行号
	printf("%s\n", __DATE__);
	printf("%s\n", __TIME__);
	printf("%d\n", __STDC__);//在有定义的时候是1
	return 0;
}

#define MAX 1000
#define reg register      //为 register这个关键字，创建一个简短的名字
#define do_forever for(;;)   //用更形象的符号来替换一种实现
#define CASE break;case     //在写case语句的时候自动把 break写上。
 如果定义的 stuff过长，可以分成几行写，除了最后一行外，每行的后面都加一个反斜杠(续行符)。
#define DEBUG_PRINT printf("file:%s\tline:%d\tdate:%s\ttime:%s\n",__FILE__,__LINE__ ,__DATE__,__TIME__ ) 
#define do_forever for(;;)

int main()
{
	printf("%d\n", MAX);
	printf("%s\n", DEBUG_PRINT);
	do_forever;

	return 0;
}

#define MAX 100;

int main()
{
	int a = MAX;
	printf("%d\n", MAX);
	return 0;
}

#define SQUARE( x ) x * x

int main()
{
	printf("%d\n", SQUARE(5));
}

#define PRINT(X) printf("the value of "#X" is %d\n",X)

void print(int x)
{
	PRINT(x);
}

int main()
{
	int a = 10;
	int b = 20;
	PRINT(a);
	PRINT(b);
	return 0;
}

int main()
{
	printf("Hello World\n");
	printf("Hello " "World\n");
	return 0;
}

#define CAT(X,Y) X##Y

int main()
{
	int student = 100;
	printf("%d\n", CAT(stu,dent));
	printf("%d\n", student);
	return 0;
}



#define MAX(a,b) ((a)>(b)?(a):(b))

float MAX2(float c, float d)
{
	return c > d ? c : d;
}

int MAX1(int a, int b)
{
	return a > b ? a : b;
}

int main()
{
	int a = 2;
	int b = 5;
	float c = 3.0f;
	float d = 4.0f;

	int max2 = MAX2(a, b);//函数调用
	printf("%f\n", max2);

	int max1 = MAX1(c, d);//函数调用
	printf("%d\n", max1);

	printf("%f\n", MAX(c, d));//宏调用
	printf("%d\n", MAX(a, b));//宏调用

	return 0;
}

#define TEST() printf("Test Successfully!\n")

int main()
{
	TEST();
	TEST();
	TEST();
}


#define MALLOC(num,type)  (type*)malloc(num*sizeof(type))

int main()
{
	int ret1 = (int*)malloc(10 * sizeof(int));
	int ret = MALLOC(10, int);
	等价于
	printf("%d\n", ret1);
	printf("%d\n", ret);

	return 0;
}

#define MAX 100

int main()
{
	printf("%d\n", MAX);
#undef MAX
	printf("%d\n", MAX);
	return 0;
}

#include "stdio.h"

int main()
{
	printf("hehe\n");
}